#pragma once

#include <iostream>
#include "sock.hpp"
#include <sys/epoll.h>


namespace ns_epollserver {
    #define PORT 8080
    using namespace ns_sock;
    class EpollServer {
        private:
            u_int16_t port_;
            int listen_sock_;
            int epfd_;
        public:
            void InitServer() {
                listen_sock_ = Sock::Socket();
                Sock::Bind(listen_sock_, port_);
                Sock::Listen(listen_sock_);

                // 创建epoll模型
                epfd_ = epoll_create(128);
                if (epfd_ < 0) {
                    std::cout << "create epoll model error" << std::endl;
                    exit(1);
                }
            }
            void HandlerServer(struct epoll_event revs[], int num) {
                for (int i = 0; i < num; ++i) {
                    int sock = revs[i].data.fd;
                    uint32_t event = revs[i].events;
                    if (event == EPOLLIN) {
                        // 如果是读事件
                        if (sock == listen_sock_) {
                            // 有新链接到来
                            struct sockaddr_in peer;
                            socklen_t len = sizeof(peer);
                            int new_sock = accept(listen_sock_, (struct sockaddr*)&peer, &len);
                            if (new_sock < 0) {
                                std::cout << "accept error" << std::endl;
                                continue;
                            }
                            // 新sock加入红黑树中
                            struct epoll_event new_ev;
                            new_ev.data.fd = new_sock;
                            new_ev.events = EPOLLIN;
                            epoll_ctl(epfd_, EPOLL_CTL_ADD, new_sock, &new_ev);
                            std::cout << "获取新链接成功,sock: " << new_sock << std::endl;
                        } else {
                            // 如果有数据到来
                            char buffer[1024];
                            int s = recv(sock, buffer, sizeof(buffer)-1, 0);
                            if (s > 0) {
                                buffer[s] = 0;
                                std::cout << "client say#  " << buffer << std::endl;
                                // 添加 关心写入事件
                                struct epoll_event ev;
                                ev.data.fd = sock;
                                ev.events = EPOLLIN | EPOLLOUT;
                                epoll_ctl(epfd_, EPOLL_CTL_MOD, sock, &ev);
                            } else if (s == 0) {
                                std::cout << "对端关闭" << std::endl;
                                close(sock);
                                // 关闭文件描述符，并从eopll模型中移除这个监听套接字
                                epoll_ctl(epfd_, EPOLL_CTL_DEL, sock, nullptr);
                            } else {
                                std::cout << "recv error " << std::endl;
                                close(sock);
                                epoll_ctl(epfd_, EPOLL_CTL_DEL, sock, nullptr);                          
                            }
                        }
                    }
                    if (event & EPOLLOUT) {
                        std::string out = "Ok";
                        send(sock, out.c_str(), out.size(), 0);
                        // 发送完消息后关闭 关心写入事件；读事件常关心，写事件按需关心
                        struct epoll_event ev;
                        ev.data.fd = sock;
                        ev.events = EPOLLIN;
                        epoll_ctl(epfd_, EPOLL_CTL_MOD, sock, &ev);
                    }
                }
            }
            void Loop() {
                // 首先将目前的listen_sock加入epoll模型（红黑树结构）里
                struct epoll_event ev;
                ev.data.fd = listen_sock_;
                ev.events = EPOLLIN;
                epoll_ctl(epfd_, EPOLL_CTL_ADD, listen_sock_, &ev);
                #define EV_NUM 10   // 需要监听的sock的数量
                struct epoll_event revs[EV_NUM];
                int timeout = -1;
                while (true) {
                    //  等待事件就绪
                    int n = epoll_wait(epfd_, revs, EV_NUM, timeout);
                    switch (n)
                    {
                    case 0:
                        /* code */
                        std::cout << "timeout " << std::endl;
                        break;
                    case -1:
                        std::cout << "epoll error" << std::endl;
                        break;
                    default:
                        HandlerServer(revs, n);
                        break;
                    }
                }
            }
            EpollServer(int port = PORT)
                :port_(port)
                ,listen_sock_(-1)
                ,epfd_(-1)
            {}
            ~EpollServer() {
                if (listen_sock_ > 0) {
                    close (listen_sock_);
                }
                if (epfd_ > 0) {
                    close(epfd_);
                }
            }
    };
}